package org.me.marble;

import java.util.*;

/**
*
* @author tungi
*/

public class BlockGenerator {
	private final float MIN_DISTANCE=5.0f;
	
	private int index=0;
	private Random random;
	private float distance=0.0f;
	
	public BlockGenerator(){
		random=new Random();
	}
	
	public void generateBlocks(ArrayList<AvailableObject> objects,
			ArrayList<Triangle> triangles,Vector gateCoordinate){
		Vector projectedPoint=new Vector(0.2f,0.0f,-5.0f);
		addNewBlock(objects,getRealPoint(triangles, projectedPoint),
				(float)Math.random()*0.5f*OptionsLibrary.difficulty_level
				/OptionsLibrary.MAX_DIFFICULTY,random.nextInt(2));
		while(true){
			if(index==triangles.size())
				break;
			//first inccrease the distance from the last block object
			float delta=Vector.Minus(triangles.get(index).getPoint(1),
						triangles.get(index).getPoint(0)).Length()+
					Vector.Minus(triangles.get(index+1).getPoint(0),
						triangles.get(index+1).getPoint(1)).Length();
			distance+=delta/2;
			//if distance id great enough, we generate a new point 
			//with 1/4 possibility
			int int1=random.nextInt(2);
			int int2=random.nextInt(2);
			if(int1!=int2 || distance<MIN_DISTANCE){
				index+=2;
				continue;
			}

			//else generate new point with generated width
			//->from wihch edge
			int from=random.nextInt(2);
			//->distance from the front of the triangle
			Vector edgePoint,normal;
			if(from==0){
				Vector v=Vector.Minus(
						triangles.get(index).getPoint(1),
						triangles.get(index).getPoint(0));
				double d=Math.random()*v.Length();
				v.Normalize();
				edgePoint=new Vector(
						triangles.get(index).getPoint(0).getX()+v.getX()*d,
						triangles.get(index).getPoint(0).getY()+v.getY()*d,
						triangles.get(index).getPoint(0).getZ()+v.getZ()*d);
				normal=Geometry.rotateVector(-Math.PI/2,v,triangles.get(index).getNormal());
			}
			else{
				Vector v=Vector.Minus(
						triangles.get(index+1).getPoint(0),
						triangles.get(index+1).getPoint(1));
				double d=Math.random()*v.Length();
				v.Normalize();
				edgePoint=new Vector(
						triangles.get(index+1).getPoint(1).getX()+v.getX()*d,
						triangles.get(index+1).getPoint(1).getY()+v.getY()*d,
						triangles.get(index+1).getPoint(1).getZ()+v.getZ()*d);
				normal=Geometry.rotateVector(Math.PI/2,v,triangles.get(index+1).getNormal());
			}
			normal.Normalize();
			//->half width of the block
			float width=(float)Math.random()*0.5f*
				(float)OptionsLibrary.difficulty_level/(float)OptionsLibrary.MAX_DIFFICULTY;
			if(width<0.1f) width=0.1f;
			//get width of the current squad
			float swidth=Vector.Minus(triangles.get(index).getPoint(0),triangles.get(index).getPoint(2)).Length();
			if(swidth<2*width)
				continue;
			//->distance from the edge
			double de=1-Math.random();
			//box will lop over the path
			if(de-width<0) 
				de=width;
			//there will be no place to marble
			if((swidth-de-2*width)<0.6f && de<0.6f) 
				de=width;
			
			projectedPoint=new Vector(edgePoint.getX()+normal.getX()*de,
									  edgePoint.getY()+normal.getY()*de,
									  edgePoint.getZ()+normal.getZ()*de);
			
			Vector realPoint=getRealPoint(triangles,projectedPoint);
			
			//check if the distance between the gate anf the generated point is small
			if(Vector.Minus(gateCoordinate,realPoint).Length()<0.5f)
				break;
			//add the new item to the object list
			addNewBlock(objects,realPoint,width,from);
			
			index+=2;
			distance=0.0f;
			projectedPoint=null;
		}
	}
	
	public Vector getRealPoint(ArrayList<Triangle> triangles,Vector projectedPoint){
		//calculate the exact coordinate
		Vector realPoint=new Vector();
		if(triangles.get(index).isPartProj(projectedPoint)){
			realPoint=Geometry.getProjectionToPlane(triangles.get(index).getNormal(), 
					triangles.get(index).getPoint(0),projectedPoint);
		}
		else if(triangles.get(index+1).isPartProj(projectedPoint)){
			realPoint=Geometry.getProjectionToPlane(triangles.get(index).getNormal(), 
					triangles.get(index).getPoint(1),projectedPoint);
		}
		return realPoint;
	}
	
	public void addNewBlock(ArrayList<AvailableObject> objects,Vector point,float width,int from){
		if(Vector.Equal(point,new Vector()))
			return;
		AvailableObject ao=new Cuboid(gameObjects.Cuboid.ordinal(),width,from);
		objects.add(ao);
        ao.centre=new Vector(point);
	}
}